// Copyright (c) 2009, Philip Tucker
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of the <organization> nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY Philip Tucker 'AS IS' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL Philip Tucker BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.honkentuber.android;

import android.location.Address;
import android.location.Location;

import com.google.android.maps.GeoPoint;

/**
 * Utilities for Geo locations.
 */
public class Geo {
  /**
   * # microdegrees per degree.
   */
  public static final int E6 = 1000000;

  // public static final int EARTH_RADIUS_KM = 6371;

  /**
   * @param location Location.
   * @return Simple comma-separated lat,long in microdegrees.
   */
  public static String toLatLonE6String(GeoPoint point) {
    return point.getLatitudeE6() + "," + point.getLongitudeE6();
  }

  /**
   * @param location Location.
   * @return Simple comma-separated lat,long in microdegrees.
   */
  public static String toLatLonE6String(Location location) {
    return toDegreesE6(location.getLatitude()) 
        + "," + toDegreesE6(location.getLongitude());
  }

  /**
   * @param location Location.
   * @return Simple comma-separated lat,long in microdegrees.
   */
  public static String toLatLonString(Location location) {
    return location.getLatitude() + "," + location.getLongitude();
  }

  /**
   * @param address Address.
   * @return Simple comma-separated lat,long in microdegrees.
   */
  public static String toLatLonE6String(Address address) {
    return toDegreesE6(address.getLatitude()) 
        + "," + toDegreesE6(address.getLongitude());
  }

  /**
   * Convert Location position to GeoPoint.
   * @param location Location.
   * @return GeoPoint.
   */
  public static GeoPoint toGeoPoint(Location location) {
    int latE6 = toDegreesE6(location.getLatitude());
    int lonE6 = toDegreesE6(location.getLongitude());
    return new GeoPoint(latE6, lonE6);
  }
  
  /**
   * Convert GeoPoint position to Location.
   * @param point GeoPoint.
   * @return Location.
   */
  public static Location toLocation(GeoPoint point) {
    Location result = new Location("");
    result.setLatitude(toDegrees(point.getLatitudeE6()));
    result.setLongitude(toDegrees(point.getLongitudeE6()));
    return result;
  }
  
  /**
   * Convert address position to GeoPoint.
   * @param address Address.
   * @return GeoPoint.
   */
  public static GeoPoint toGeoPoint(Address address) {
    int latE6 = toDegreesE6(address.getLatitude());
    int lonE6 = toDegreesE6(address.getLongitude());
    return new GeoPoint(latE6, lonE6);
  }

  /**
   * Convert degrees to microdegrees.
   * @param degrees Value in degrees.
   * @return Value in microdegrees.
   */
  public static int toDegreesE6(double degrees) {
    return (int) Math.round(degrees * E6);
  }

  /**
   * Convert microdegrees to degrees.
   * @param degreesE6 Value in microdegrees.
   * @return Value in degrees.
   */
  public static double toDegrees(int degreesE6) {
    return (double) degreesE6 / E6;
  }
  
  /**
   * @param address Address.
   * @return Short name composed of the most specific data available.
   */
  public static String toShortName(Address address) {
    StringBuilder result = new StringBuilder();
    result.append(Strings.safeTrim(address.getFeatureName()));
    if (result.length() == 0) {
      for (int i = 0; i < address.getMaxAddressLineIndex(); ++i) {
        if (result.length() > 0) {
          result.append(",");
        }
        result.append(Strings.safeTrim(address.getAddressLine(i)));
      }
    }
    if (result.length() == 0) {
      result.append(Strings.safeTrim(address.getLocality()));
    }
    if (result.length() == 0) {
      result.append(Strings.safeTrim(address.getPostalCode()));
    }
    if (result.length() == 0) {
      result.append(Strings.safeTrim(address.getSubAdminArea()));
    }
    if (result.length() == 0) {
      result.append(Strings.safeTrim(address.getAdminArea()));
    }
    if (result.length() == 0) {
      result.append(Strings.safeTrim(address.getCountryName()));
    }
    if (result.length() == 0) {
      result.append(Strings.safeTrim(address.getCountryCode()));
    }
    return result.toString();
  }

  /**
   * @param address Address.
   * @return Formatted postal address with as many details as possible.
   */
  public static String toPostalAddress(Address address) {
    StringBuilder result = new StringBuilder();
    boolean atLineStart = true;

    for (int i = 0; i < address.getMaxAddressLineIndex(); ++i) {
      result.append(Strings.safeTrim(address.getAddressLine(i)));
      result.append('\n');
    }

    String locality = Strings.safeTrim(address.getLocality());
    if (locality.length() > 0) {
      result.append(locality);      
      atLineStart = false;
    }

    String subAdminArea = Strings.safeTrim(address.getSubAdminArea());
    if (subAdminArea.length() > 0) {
      if (!atLineStart) {
        result.append(", ");
      }
      result.append(subAdminArea);      
      atLineStart = false;
    }

    String adminArea = Strings.safeTrim(address.getAdminArea());
    if (adminArea.length() > 0) {
      if (!atLineStart) {
        result.append(", ");
      }
      result.append(adminArea);      
      atLineStart = false;
    }

    String country = Strings.safeTrim(address.getCountryCode());
    if (country.length() == 0) {
      country = Strings.safeTrim(address.getCountryName());
    }
    if (country.length() > 0) {
      if (!atLineStart) {
        result.append(", ");
      }
      result.append(country);      
      atLineStart = false;
    }

    String zip = Strings.safeTrim(address.getPostalCode());
    if (zip.length() > 0) {
      if (!atLineStart) {
        result.append("  ");
      }
      result.append(zip);      
      atLineStart = false;
    }

    return result.toString();
  }
}